<?php

namespace Application;

use Fluency\Web;
use Fluency\Web\Http;
use Fluency\Web\Routing;
use Fluency\Utils;

// The directory corresponding to the namespace of the application
define(__NAMESPACE__ . "\\DIR_BASE", __DIR__ . "/Application");
// URL for this file
define(__NAMESPACE__ . "\\URL_BASE", $_SERVER["SCRIPT_NAME"]);

// Include the file containing the ClassLoader class, which is used for autoloading.
require_once __DIR__ . "/Fluency/Utils/ClassLoader.php";

// The display_errors and error_reporting settings should be adjusted before deploying.
ini_set('display_errors', true);
error_reporting(E_ALL | E_STRICT);

// Set the default timezone
date_default_timezone_set("UTC");

// Map namespaces to directories for autoloading
spl_autoload_register(new Utils\ClassLoader("Fluency", __DIR__ . "/Fluency"));
spl_autoload_register(new Utils\ClassLoader(__NAMESPACE__, DIR_BASE));

// Register an error handler which converts PHP errors to exceptions
set_error_handler(function($code, $msg, $file, $line) {
    if (error_reporting()) {
        throw new \ErrorException($msg, $code, $code, $file, $line);
    }
});

// The following code is wrapped in a closure in order not to pollute the global namespace.
call_user_func(function() {

    // Build a RequestDispatcher object
    $dispatcher = Web\RequestDispatcher::getBuilder()
        // The action resolver takes an action name and returns the action corresponding
        // to it. If no such action exists, it should throw an InvalidArgumentException.
        // Note that the action name MUST BE TREATED AS UNTRUSTED INPUT and sanitized properly.
        ->setActionResolver(function($name) {
            $file = DIR_BASE . "/Actions/{$name}.php";
            if (preg_match('/[^A-Za-z_\-\/]/', $name) || !is_file($file)) {
                throw new \InvalidArgumentException;
            }
            return require $file;
        })
        // The filter resolver works for filters just like the action resolver works for actions,
        // except that no availability checks are required for filters and that it receives a
        // trusted input, which comes from action annotations.
        ->setFilterResolver(function($name) {
            return require DIR_BASE . "/Filters/{$name}.php";
        })
        // The view resolver takes a name of a view and an instance of ActiveRequest and returns
        // a view, which is a function that takes a single parameter and renders it by returning
        // or echoing (or both) a string.
        ->setViewResolver(function($_name, Http\ActiveRequest $req) {
            return function($input) use ($_name, $req) {
                require DIR_BASE . "/Views/{$_name}.php";
            };
        })
        ->setGlobalFilters(array(
            // Names of global filters (filters that are applied to all actions) should go here.
        ))
        // Finish the building process
        ->build();

    // Construct a chain router which queries all other routers in the parameter array and returns
    // the first non-null result (the Request object).
    $router = new Routing\ChainRouter(array(
        // PathRouter treats URLs to action names with request parameter key/value pairs appended.
        new Routing\PathRouter($dispatcher),
        // IndexRouter attaches an optionally specified string (defaults to "index") to the URL and
        // treats it as an action name.
        new Routing\IndexRouter($dispatcher),
        // FallbackRouter always returns the same result (a fallback request).
        new Routing\FallbackRouter($dispatcher->createRequest()
                                              ->forAction("error")
                                              ->withParam("code", 404)
                                              ->withInternalScope()),
    ));

    // Query the router to create a request for the PATH_INFO segment of the current URI
    $request = $router->query(@$_SERVER["PATH_INFO"]);

    // The main request is dispatched here, so the majority of exceptions are also thrown here.
    try {
        echo $request->execute()->sendHeaders()->getBody();
    } catch (\Exception $e) {
        // TODO: implement proper exception handling
        throw $e;
    }

});
